#ifndef SIMULATION_H
#define SIMULATION_H
/*Project 1 - Simulate a single customer queue : M/M/1/K queue
 * Author : Saurabh V. Pendse
 * Unity ID : svpendse
 * Student ID : 001026185
 */
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <stdint.h>
#include <time.h>
#define IA 16807
#define IM 2147483647
#define AM (1.0/IM)
#define IQ 127773
#define IR 2836
#define MASK 123459876
using namespace std;

class Simulation {
public:
	long kSeed_arrival;				//seed for the generating the interarrival times
	long kSeed_service;				//seed for the generating the service times

	struct Customer;
	struct EventNode;

	vector<Customer *> customer_list;	//the list of all customers (arrived + lost)
	vector<EventNode *> event_list;		//the event list
	queue<Customer *> customer_queue;	//the customer queue

	//Customer structure
	typedef struct Customer {
		uint32_t id_;					//the customer ID
		double arrival_time_;			//the arrival time
		double service_time_;			//the service time
		double waiting_time_;			//the waiting time
		double departure_time_;			//the departure time
		uint32_t customers_after_;		//the number of customers in the system immediately after this customer 
		bool lost_, serviced_;			//flags to indicate whether the customer has been served yet, or has 									 
		//been lost
		uint32_t departure_number_;
		//constructor to initialize a Customer
		Customer(uint32_t id, double arrival_time, double service_time,
				double waiting_time, double departure_time,
				bool serviced, bool lost) : id_(id), arrival_time_(arrival_time),
		service_time_(service_time), waiting_time_(waiting_time), departure_time_(departure_time),
		serviced_(serviced), lost_(lost) {
			customers_after_ = 0;
			departure_number_ = -1;
		}
		//utility function to print the customer information
		void print() {
			cout << "Id : " << id_ << ", Arrival time = " << arrival_time_
				<< ", Service time = " << service_time_
				<< ", Waiting time = " << waiting_time_
				<< ", Departure time = " << departure_time_
				<< ", Lost = " << lost_
				<< ", serviced = " << serviced_ 
				<< ", customers after = " << customers_after_ << endl;
		}
	}Customer;

	typedef struct EventNode {
		char type_;						//event type ('A', 'D' or 'L')
		Customer *customer_;			//pointer to the corresponding customer
		double time_;					//the time of the event
		//constructor to initialize an event
		EventNode(char type, Customer *customer, double time) : type_(type),
		customer_(customer), time_(time) {
		}
		//utility function to print the event information
		void print() {
			if (customer_ != NULL) {
				cout << "Type : " << type_ << ", id : " << customer_->id_
					<< ", time : " << time_ << endl; //", queue size = " << customer_queue.size() << endl;
			}
		}

	}EventNode;
	double lambda, mu;					//parameters for the interarrival and service time exp. dist.
	uint32_t k, c, l;					//k = queue capacity, c = customers to be served, l = parameter
	double Ta, Ts;						//The interarrival and service times
	int n;								//number of customers in the queue
	double t1, t2, te;					//times to run the simulation
	uint32_t arrival_count, departure_count, total_arrival_count, total_departure_count;		//counters
	uint32_t serve_count;				//the number of customers served
	double master_clock;				//the master clock
	double sim_time;					//the simulation time



	Simulation(int argc, char **argv);

	//function to generate the random number in the range (0, 1), given a seed
	static double ran0(long *idnum);

	//function to generate an exponentially distributed random number with mean 1
	static double expdev(long *idnum);

	//function to generate an exponentially distributed random number with mean "mean"
	static double expntl(double mean, long &seed);

	//checks whether the event list is empty
	bool event_list_empty();

	//returns the current size of the event list
	uint32_t event_list_size();

	//returns the first element (i.e. the next event) from the event list
	EventNode *event_list_front();

	//adds a new element (i.e. a new event) to the event list, ordered by the time of the event
	void event_list_add(EventNode *eventNode);

	//removes an element (i.e. the next event) from the event list
	EventNode *event_list_remove_front();

	//prints the contents of the event list
	void event_list_print();

	//run the simulation until c customers are served
	void run();
	
	//schedule a departure event for a given customer
	void schedule_departure(Customer *customer);

	//process an arrival event
	void process_arrival(EventNode *next_event);

	//process the lost event
	void process_lost(EventNode *next_event);

	//process the departure event
	void process_departure(EventNode *next_event);
	
	void calculate_statistics(); 

	void customer_diagnostics(uint32_t index);
};
#endif
